# -*- coding: utf-8 -*-
import flask
import logging
import urllib
import urllib2
import collections
import json
from google.appengine.api import users
from decorators import login_required, admin_required
from flaskext import wtf

from celestra import app
import payson
import signup
import interested  # to have these data models included

payson_api = payson.PaysonApi(app.config['PAYSON_AGENT_ID'],
                              app.config['PAYSON_AGENT_KEY'])

from google.appengine.api import memcache
import functools


def cached(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        if app.config.get('TESTING'):
            return func(*args, **kwargs)
        key = func.__name__
        val = memcache.get(key)  # @UndefinedVariable
        logging.debug('Cache lookup for %s, found: %s', key, val != None)
        if not val:
            val = func(*args, **kwargs)
            memcache.set(key, val, time=3600)  # @UndefinedVariable
        return val

    return wrapper


@app.route('/')
def front():
    from celestra import team
    members = team.teams
    return flask.render_template('start.html',
                                 members=members)


@app.route('/installproducts/')
@admin_required
def install_products():
    from products import install_products
    install_products()
    return 'OK'


@app.route('/signup/', methods=['GET', 'POST'])
def sign_up():
    form = signup.SignUpForm()
    if form.validate_on_submit():
        if form.message.data:
            return flask.redirect('/')
        order = form.save()
        return flask.redirect('/payment/?okey=%s&stfb=%s' % (str(order.key()),
                                          str(form.share_to_facebook.data).lower()))
    form.game.checked = 'checked'
    return flask.render_template('signup.html',
                                 form=form,
                                 action='/signup/')


@app.route('/signup/availability/')
def get_availability():
    return flask.jsonify(signup.get_available_cc())


@app.route('/payment/update_orders/')
@admin_required
def update_orders():
    signup.check_accepted_orders()
    return 'OK'


@app.route('/payment/', methods=['GET', 'POST'])
def payment_page():
    form = signup.PaymentForm()
    if flask.request.method == 'POST':
        if form.validate():
            return form.save(payson_api)
        else:
            order_key = form.order_key.data
            stfb = json.loads(form.share_to_facebook.data)
    else:
        order_key = flask.request.args.get('okey', '')
        stfb = json.loads(flask.request.args.get('stfb', 'false'))
    order = signup.Order.get(order_key)
    if not order:
        flask.flash(u'The requested order could not be found.', 'error')
        return flask.redirect('/signup/')
    form.order_key.data = str(order.key())
    form.share_to_facebook.data = str(stfb).lower()
    if order.remainder <= 0:
        form.payment_method.validators = [wtf.Optional(), ]
    return flask.render_template('payment.html',
                                 form=form,
                                 order=order,
                                 action='/payment/')


@app.route('/signup/complete/')
def confirmation():
    memcache.delete('available_cc')
    return flask.render_template('confirmation.html')


@app.route('/interest/all/')
def interest_list():
    if users.is_current_user_admin():
        q = interested.InterestedUser.all()
        q.order('date')
        all_interested = [a for a in q]
        total = interested.total_donated_amount()
        return flask.render_template('interested_list.html',
                                     users=all_interested,
                                     total_amount=total)
    else:
        return flask.redirect(users.create_login_url(flask.request.url))


@app.route('/payment/return')
def payment_return():
    payment = signup.PaysonPayment.get_from_token(flask.request.args)
    r = None
    if not payment:
        flask.flash('Error, payment record not found. '
                    'Contact tomas@alternaliv.se for help.', 'error')
        return flask.render_template('signup.html',
                                     form=signup.SignUpForm(),
                                     action='/signup/')
    else:
        try:
            r = payson_api.payment_details(payment.token)
        except urllib2.URLError:
            pass
        else:
            if r.success and payment.status != 'COMPLETED':
                payment.update(r)

    if payment.status == 'COMPLETED':
        signup.send_email(payment.order, payment)
        flask.flash(signup.messages['payment ok'] % payment.amount,
                    'success')
    elif payment.status == 'ERROR':
        flask.flash(signup.messages['payment error'], 'error')
        return flask.redirect(signup.payment_page_url(payment.order, bool(r and r.custom)))
    elif payment.status == 'PROCESSING':
        flask.flash(u'The response from Payson was inconclusive. Please select invoice instead, but before paying, '
                    u'check your bank statement and make sure this payment didn\'t go through.')
        return flask.redirect(signup.payment_page_url(payment.order, bool(r and r.custom)))

    if r and r.custom:
        return flask.redirect(signup.share_to_facebook_url)
    else:
        return flask.redirect('/signup/complete/')



@app.route('/payment/cancel')
def payment_cancel():
    payment = signup.PaysonPayment.get_from_token(flask.request.args)
    if payment.status != 'COMPLETED':
        flask.flash(u'Your Payson payment was cancelled. Please select another option', 'warning')
        try:
            r = payson_api.payment_details(payment.token)
        except urllib2.URLError:
            pass
        if r.success:
            payment.update(r)

    return flask.redirect(signup.payment_page_url(payment.order, bool(r and r.custom)))


@app.route('/payment/ipn', methods=['POST'])
def payment_ipn():
    try:
        if (flask.request.method == 'POST' and
            payson_api.validate(urllib.urlencode(
                                     flask.request.form.items(True)))):
            data = flask.request.form.to_dict()
            logging.info('IPN callback: {0}'.format(data))
            paydetails = payson.PaymentDetails(data)
            payment = signup.PaysonPayment.get(paydetails.trackingId)
            if payment.status != 'COMPLETED':
                payment.update(paydetails)
        else:
            logging.info('Unvalidated IPN callback: {0}'\
                         .format(flask.request.data))
    except Exception, e:
        logging.error('IPN handler exception: {0}, '
                      'Form data: {1}'.format(e, flask.request.data))
    finally:
        return ''


@app.route('/payment/register/', methods=['GET', 'POST'])
@admin_required
def register_payment():
    form = signup.PaymentRegistrationForm()
    if form.validate_on_submit():
        form.save()
    form.reference.data = None
    form.reference.raw_data = None
    form.amount.data = None
    form.amount.raw_data = None
    return flask.render_template('/register_payment.html',
                                 form=form,
                                 action='/payment/register/')


Obj = collections.namedtuple('Obj',
                             'key '
                             'email '
                             'name '
                             'game '
                             'cc '
                             'date '
                             'debt '
                             'com1 '
                             'com2 '
                             'com3 '
                             'accepted'
                              )
@app.route('/signup/all/')
@admin_required
def signup_all():
    from products import game_choices_d, game_dates
    accepted = flask.request.args.get('accepted', False)
    revoked = flask.request.args.get('revoked', False)
    q = signup.Order.all()
    if accepted:
        q.filter('accepted', True)
    if not revoked:
        q.filter('revoked IN', (False, None))
    q.order('creation_time')

    def objects():
        for o in q:
            cc = next((i.product.key().name()
                       for i in o.items
                       if i.product.key().name() in game_choices_d), None)
            g = game_dates[cc[:2]]
            cc = cc[-1].upper()
            yield \
                Obj(str(o.key()),
                    o.player.email,
                    ' '.join((o.player.firstname, o.player.lastname)),
                    g,
                    cc,
                    o.creation_time,
                    o.remainder,
                    o.character_comments,
                    o.coplayer_comments,
                    o.other_comments,
                    'Yes' if o.accepted else 'No')
    return flask.render_template('signup_list.html',
                                 objects=objects())


@app.route('/order/edit/', methods=['GET', 'POST'])
@admin_required
def edit_order():
    form = signup.OrderEditForm()
    order_key = flask.request.args.get('order_key',
                                       form.key.data)
    order = signup.Order.get(order_key)
    form.set_player_choices(order)
    if form.validate_on_submit():
        form.save()
        flask.flash('Order updated.', 'success')
    elif 'order_key' in flask.request.args:
        form.populate(order)
    return flask.render_template('edit_order.html',
                                 form=form,
                                 order=order,
                                 action='/order/edit/')

# # Error handlers
# Handle 404 errors
@app.errorhandler(404)
def page_not_found(e):
    return flask.render_template('404.html'), 404


# Handle 500 errors
@app.errorhandler(500)
def server_error(e):
    return flask.render_template('500.html'), 500
